﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Drawing;
using System.Net;
using System.Net.Sockets;
using System.Threading;

using TracedReality;
using TracedReality.RayTracing;

namespace RenderServer
{
    class WorkerServer
    {
        private BroadcastServer caster;
        private IPEndPoint endpoint;
        private TcpListener tcpListener;
        private Thread listener;

        private bool stop;

        public WorkerServer(int port, int bcport)
        {
            caster = new BroadcastServer(bcport);

            endpoint = new IPEndPoint(IPAddress.Any, port);
            tcpListener = new TcpListener(endpoint);
            tcpListener.AllowNatTraversal(true);

            listener = new Thread(listen);

            stop = false;
        }

        public void Start()
        {
            stop = false;
            caster.Start();
            listener.Start();

            Console.WriteLine("Listening on port {0}", endpoint.Port);
        }

        public void Stop()
        {
            stop = true;
            caster.Stop();
        }

        private void listen()
        {
            tcpListener.Start();

            while (!stop)
            {
                TcpClient client = tcpListener.AcceptTcpClient();
                Console.WriteLine("Client connected: {0}", client.Client.RemoteEndPoint);
                handleClient(client);
            }

            tcpListener.Stop();
        }

        private void handleClient(TcpClient client)
        {
            caster.Pause();
            NetworkStream stream = client.GetStream();

            string data;
            byte[] message;
            int bytesRead;
            Scene scene;
            double maxdim;
            Random rand = new Random();

            // Handshake - send my version
            message = Encoding.ASCII.GetBytes(RayTracer.VERSION);
            stream.Write(message, 0, message.Length);
            stream.Flush();

            // Response - invalid version or a scene
            message = new byte[8192];
            bytesRead = stream.Read(message, 0, message.Length);

            if (bytesRead == 0)
            {
                Console.WriteLine("Error reading (1)");
                client.Close();
                caster.Resume();
                return;
            }

            data = Encoding.ASCII.GetString(message, 0, bytesRead);
            if (data == "Disconnect")
            {
                Console.WriteLine("Incorrect version");
                client.Close();
                caster.Resume();
                return;
            }

            scene = Scene.parseScene(data);
            maxdim = Math.Max(Settings.width, Settings.height);

            // Receive work and do work loop
            while(true)
            {
                bytesRead = stream.Read(message, 0, message.Length);
                if (bytesRead == 0)
                {
                    Console.WriteLine("Error reading (2)");
                    break;
                }

                data = Encoding.ASCII.GetString(message, 0, bytesRead);
                if (data == "Disconnect") { break; }

                // get coords
                Utilities.Tile tile = null;
                
                // render
                Color[,] result = RayTracer.traceArea(tile, maxdim, rand);

                // send result
                Bitmap img = new Bitmap(tile.width, tile.height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
                for (int x = 0; x < tile.width; x++)
                {
                    for (int y = 0; y < tile.height; y++)
                    {
                        img.SetPixel(x, y, result[x, y]);
                    }
                }
                img.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
            }

            client.Close();
            caster.Resume();
        }
    }
}
